﻿/////////////////////////////////////////////////////////////////////////
// MIT License
//
// Copyright (c) 2000-2011 OpenNETCF Consulting, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  
////////////////////////////////////////////////////////////////////////

#region Using directives

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Collections;
using System.Text;

#endregion

namespace OpenNETCF.Windows.Forms
{
    public class PopupMenuItem : MenuItem 
    {
        #region events
        
        /// <summary>
        ///  Occurs when the menu item is clicked or selected using a shortcut key or access key defined for the menu item.
        /// </summary>
        public new event System.EventHandler Click;
        
        /// <summary>
        ///  Occurs when a PopupMenuItem is drawn and the OwnerDraw property is set to true.
        /// </summary>
        public event DrawMenuItemEventHandler DrawMenuItem;
 
        /// <summary>
        ///  Occurs when the menu needs to know the size of a menu item before drawing it.
        /// </summary>
        public event MeasureItemEventHandler MeasureItem;

        #endregion

        #region fields

        private string text;       
        private PopupMenu.PopupMenuItemCollection itemList;
        private int imageIndex;
        private Font font;
        private Color foreColor;
        private Pen blackPen;

        internal IntPtr m_hParent;
        internal PopupMenu menuBase;
        internal Menu parentMenu;
        internal int nativeIndex = 0; 
        private bool ownerDraw;
        protected const int DPISETTING = 96;

        #endregion

        #region constructors
        
        /// <summary>
        /// Initializes a new instance of the PopupMenuItem class.
        /// </summary>
        public PopupMenuItem()
        {
            itemList = new PopupMenu.PopupMenuItemCollection(this);
            imageIndex = -1;
            ownerDraw = false;
            font = new Font("Tahoma", 8F, FontStyle.Bold);
            foreColor = Color.Black;
            blackPen = new Pen(Color.Black);
        }

        /// <summary>
        ///  Initializes a new instance of the PopupMenuItem class with a specified caption for the menu item.
        /// </summary>
        /// <param name="text"></param>
        public PopupMenuItem(string text)
            : this()
        {
            //itemList = new ArrayList();
            this.text = text;
            imageIndex = -1;
            ownerDraw = false;
        } 

        #endregion

        #region helper methods

        private void MeasureItemInternal(MeasureItemEventArgs e)
        {
            PopupMenuItem item = this;

            if (item.Text == "-") //Separator
            {
                return;
            }

            SizeF size = this.MeasureString(item.Text, item.Font);
            menuBase.menuWidth = Math.Max((int)size.Width, menuBase.menuWidth);

            e.ItemHeight = (int)size.Height + 6;
            e.ItemWidth = menuBase.menuWidth + 20;

            if (menuBase.ImageList != null)
            {
                if (item.ImageIndex > (-1) && menuBase.ImageList.Images[item.ImageIndex] != null)
                {
                    e.ItemWidth += menuBase.ImageList.ImageSize.Width;// +30;
                }
            }
        }     

        private void DrawItemInternal(DrawMenuItemEventArgs e)
        {
            #region owner draw

            System.Drawing.Imaging.ImageAttributes imageAttr = new ImageAttributes();
            Brush textBrush = null;

            PopupMenuItem item = this;
                
            bool separator = false;

            if (this.Text == "-") // Separator
            {
                separator = true;
            }


            if (e.State == DrawItemState.Selected && separator != true)
            {
                e.DrawBackground(SystemColors.Highlight);
                textBrush = new SolidBrush(SystemColors.HighlightText);
            }
            else
            {
                if (menuBase.BackgroundImage != null)
                {
                    e.Graphics.DrawImage(menuBase.BackgroundImage, e.Bounds, e.Bounds, GraphicsUnit.Pixel);                        
                }
                else
                {
                    e.DrawBackground(e.BackColor);
                }

                textBrush = new SolidBrush(item.ForeColor);
            }

            if (!this.Enabled)
            {
                textBrush.Dispose();
                textBrush = new SolidBrush(SystemColors.InactiveCaption);
            }

            if (separator) // Separator
            {
                e.Graphics.DrawLine(blackPen, 0, e.Bounds.Top + e.Bounds.Height / 2, e.Bounds.Width, e.Bounds.Top + e.Bounds.Height / 2);
                return;
            }

            int scale = (int)e.Graphics.DpiX / DPISETTING;

            Rectangle rcText = new Rectangle(10, e.Bounds.Top + 2, e.Bounds.Width, e.Bounds.Height - 2);
            if (menuBase.ImageList != null)
            {
                //Draw image
                if (item.ImageIndex > (-1) && menuBase.ImageList.Images[item.ImageIndex] != null)
                {
                    Image image = menuBase.ImageList.Images[item.ImageIndex];
                    Color color = GetBackColor(image as Bitmap);
                    imageAttr.SetColorKey(color, color);
                    Rectangle imageRect = new Rectangle(e.Bounds.Left + 2, e.Bounds.Top + 1, image.Width * scale, image.Height * scale);
                    e.Graphics.DrawImage(image, imageRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttr);
                    rcText = new Rectangle(e.Bounds.Left + image.Width * scale + 8, e.Bounds.Top + 2, e.Bounds.Width, e.Bounds.Height - 2);
                }
            }

            // Draw text
            e.Graphics.DrawString(item.Text, item.Font, textBrush, rcText);
            textBrush.Dispose();

            #endregion
        }

        private Color GetBackColor(Bitmap image)
        {
            return image.GetPixel(0, 0);
        }

        internal PopupMenu GetBaseMenu(PopupMenuItem item)
        {
            if (item == null)
                return null;

            if (item.Parent is PopupMenu)
            {
                return item.Parent as PopupMenu;
            }
            else
            {
                return GetBaseMenu(item.Parent as PopupMenuItem);
            }
            //return null;
        }

        internal PopupMenu GetBaseMenu()
        {
            if (this.Parent is PopupMenu)
            {
                return this.Parent as PopupMenu;
            }
            else
            {
                GetBaseMenu(this.Parent as PopupMenuItem);
            }
            return null;
        }

        internal SizeF MeasureString(string text, Font font)
        {
            Native.SIZE sz = new Native.SIZE();
            int fit = 0;
            IntPtr hDC = Native.CreateCompatibleDC(IntPtr.Zero);
            IntPtr hOldFont = Native.SelectObject(hDC, font.ToHfont());
            int width = 100;
            Native.GetTextExtentExPoint(hDC, text, text.Length, width, out fit, null, ref sz);
            Native.SelectObject(hDC, hOldFont);
            Native.DeleteObject(hDC);
            return new SizeF(sz.width, sz.height);
        }

        internal void WmDrawItem(DrawMenuItemEventArgs e)
        {
            this.OnDrawItem(e);
        }

        internal void WmMeasureItem(MeasureItemEventArgs e)
        {
            this.OnMeasureItem(e);
        }

        internal void RaiseClick(EventArgs e)
        {
            OnClick(e);
        }

        #endregion

        #region virtual methods

        /// <summary>
        /// Raises the DrawItem event.
        /// </summary>
        /// <param name="e">A DrawItemEventArgs that contains the event data.</param>
        protected virtual void OnDrawItem(DrawMenuItemEventArgs e)
        {
            if (ownerDraw)
            {
                if (this.DrawMenuItem != null)
                {
                    this.DrawMenuItem(this, e);
                }
            }
            else
                this.DrawItemInternal(e);
        }

        /// <summary>
        /// Raises the MeasureItem event.
        /// </summary>
        /// <param name="e">A MeasureItemEventArgs that contains the event data.</param>
        protected virtual void OnMeasureItem(MeasureItemEventArgs e)
        {
            if (this.MeasureItem != null)
            {               
                this.MeasureItem(this, e);
            }
            else
            {
                this.MeasureItemInternal(e);
            }
        }

        /// <summary>
        /// Raises the Click event.
        /// </summary>
        /// <param name="e">An EventArgs that contains the event data.</param>
        protected override void OnClick(EventArgs e)
        {
            if (Click != null)
            {
                Click(this, e);
            }
        }

        #endregion

        #region properties

        /// <summary>
        ///  Gets a value indicating the menu that contains this menu item.
        /// </summary>
        public new Menu Parent 
        { 
            get
            {
                return parentMenu;
            }         
        }
        /// <summary>
        /// Gets a value indicating the collection of PopupMenuItem objects associated with the menu.
        /// </summary>
        public new PopupMenu.PopupMenuItemCollection MenuItems
        {
            get { return itemList; }
        }


        internal Menu MenuBase
		{
			get
			{
				return menuBase;
			}
			set
			{
                if (value is PopupMenu)
                {
                    menuBase = value as PopupMenu;
                    parentMenu = menuBase;
                    itemList.menuBase = menuBase;
                }
                else
                {
                   
                    PopupMenuItem menuItem = (PopupMenuItem)value;
                    parentMenu = menuItem;                   
                    menuBase = GetBaseMenu(menuItem);                    
                }
			}
		}

        /// <summary>
        ///  Gets or sets a value indicating whether the code that you provide draws the menu item or Windows draws the menu item.
        /// </summary>
        public bool OwnerDraw 
        {
            get { return ownerDraw; }
            set { ownerDraw = value; } 
        }

        /// <summary>
        /// Gets or sets the Font for the menu item.
        /// </summary>
        public Font Font
        {
            get { return font; }
            set { font = value; }
        }

        /// <summary>
        /// Gets or sets the index of the icon from the ListView to display.
        /// </summary>
        public int ImageIndex
        {
            get { return imageIndex; }
            set { imageIndex = value; }
        }

        /// <summary>
        /// Gets or sets the color of the text.
        /// </summary>
        public Color ForeColor
        {
            get { return foreColor; }
            set { foreColor = value; }
        }

        /// <summary>
        ///  Gets or sets a value indicating the caption of the menu item.
        /// </summary>
        public new string Text
        {
            get { return text; }
            set
            {
                text = value;
                base.Text = value;
            }
        }

        public new bool Enabled
        {
            get
            {
                return base.Enabled;
            }
            set
            {
                base.Enabled = value;
                if(menuBase!=null)
                    menuBase.EnableMenuItem(this);
            }
        }
        #endregion

    }

}
